﻿/* 2014/11/10 */
using System;
using System.Collections.Generic;
using System.IO;

namespace Cosmos.IO.FileSystemPath.Searcher
{
    /// <summary>
    /// Abstract class.
    /// Provides base implement of searching file or directory
    /// by checking FileSystemInfo (FileInfo or DirectoryInfo).
    /// </summary>
    public abstract class FileSystemInfoPathSearcher : IPathSearcher
    {
        private bool _isPatternNullOrEmpty;

        public virtual string[] Search(string searchPattern, string directoryPath,
            SearchFileDirectoryOption searchFileDirectoryOption, bool invertMatch)
        {
            _isPatternNullOrEmpty = String.IsNullOrEmpty(searchPattern);

            if (!_isPatternNullOrEmpty)
            {
                Prepare(searchPattern);
            }

            DirectoryInfo directory = new DirectoryInfo(directoryPath);
            List<string> pathList = new List<string>();

            bool containDirectory = false;
            bool containFile = false;
            switch (searchFileDirectoryOption)
            {
                case SearchFileDirectoryOption.None:
                    containFile = false;
                    containDirectory = false;
                    break;
                case SearchFileDirectoryOption.File:
                    containFile = true;
                    containDirectory = false;
                    break;
                case SearchFileDirectoryOption.Directory:
                    containFile = false;
                    containDirectory = true;
                    break;
                case SearchFileDirectoryOption.All:
                    containFile = true;
                    containDirectory = true;
                    break;
                default:
                    containFile = true;
                    containDirectory = false;
                    break;
            }

            FileInfo[] allFiles;
            DirectoryInfo[] allDirectories;
            SearchAllPath(directory, out allFiles, out allDirectories,
                containDirectory, containFile);

            if (containDirectory)
            {
                allDirectories = directory.GetDirectories();
                pathList.Capacity = allDirectories.Length;
                foreach (DirectoryInfo aDirectory in allDirectories)
                {
                    if (_isPatternNullOrEmpty || (invertMatch ^ Match(aDirectory)))
                    {
                        pathList.Add(aDirectory.FullName);
                    }
                }
            }

            if (containFile)
            {
                allFiles = directory.GetFiles();
                pathList.Capacity = Math.Max(pathList.Capacity,
                    pathList.Count + allFiles.Length);
                foreach (FileInfo aFile in allFiles)
                {
                    if (_isPatternNullOrEmpty || (invertMatch ^ Match(aFile)))
                    {
                        pathList.Add(aFile.FullName);
                    }
                }
            }            

            return pathList.ToArray();
        }

        /// <summary>
        /// Prepares for matching file or directory info by
        /// search pattern. Search pattern will not be null
        /// or empty.
        /// </summary>
        /// <param name="searchPattern">Search pattern.</param>
        /// <exception cref="ArgumentException">Search pattern is invalid.</exception>
        protected abstract void Prepare(string searchPattern);

        /// <summary>
        /// Determines whether file or directory info is matched
        /// by search pattern.
        /// </summary>
        /// <param name="info">Checked file or directory info.</param>
        /// <returns>true if matches, otherwise false.</returns>
        protected abstract bool Match(FileSystemInfo info);

        private void SearchAllPath(DirectoryInfo directory, out FileInfo[] allFiles,
            out DirectoryInfo[] allDirectories, bool containDirectory, bool containFile)
        {
            allFiles = null;
            allDirectories = null;
            if (containDirectory)
            {
                allDirectories = directory.GetDirectories();
            }
            if (containFile)
            {
                allFiles = directory.GetFiles();
            }
        }

        private void AddPathToList(FileSystemInfo[] infos, List<string> pathList)
        {
            pathList.Capacity = Math.Max(pathList.Capacity, pathList.Count + infos.Length);
            foreach (FileSystemInfo anInfo in infos)
            {
                pathList.Add(anInfo.FullName);
            }
        }

        protected bool IsPatternNullOrEmpty
        {
            get
            {
                return _isPatternNullOrEmpty;
            }
        }
    }
}
